
#include <operator.h>
#include <lexer.h>

static Operator token_to_operator[1024] = {
    [kTokenOpComma] = kOpComma,        // ,
    [kTokenDot] = kOpMemberAccess,  // .
    [kTokenLeftBrack] = kOpArraySubscript,    // [
    [kTokenRightBrack] = kOpArraySubscript,    // ]

    [kTokenPlus] = kOpAdd,      // +
    [kTokenMinus] = kOpSub,  // -
    [kTokenStar] = kOpMult,   // *
    [kTokenOpDiv] = kOpDiv,      // /
    [kTokenOpReminder] = kOpReminder,      // %
    [kTokenSelfInc] = kOpSelfIncSufix,  // ++
    [kTokenSelfDec] = kOpSelfDecSufix,  // --
    [kTokenSizeof] = kOpSizeof,

    [kTokenOpGreat] = kOpGreatThan,     // >
    [kTokenOpLess] = kOpLessThan,      // <
    [kTokenOpEqual] = kOpEqual,     // ==
    [kTokenOpGreatEq] = kOpGreatEqual,   // >=
    [kTokenOpLessEq] = kOpLessEqual,    // <=
    [kTokenOpNotEq] = kOpNotEqual,  // !=

    [kTokenOpLogicAnd] = kOpLogicAnd,  // &&
    [kTokenOpLogicOr] = kOpLogicOr,    // ||
    [kTokenOpLogicNot] = kOpLogicNot,  // !

    [kTokenAmp] = kOpBitAnd,     // &
    [kTokenOpBitOr] = kOpBitOr,       // |
    [kTokenOpBitNot] = kOpBitNot,     // ~
    [kTokenOpBitXor] = kOpBitXor,     // ^
    [kTokenOpShiftLeft] = kOpShiftLeft,   // <<
    [kTokenOpShiftRight] = kOpShiftRight,  // >>

    [kTokenOpAssign] = kOpAssign,  // =

    [kTokenOpAddAssign] = kOpAddAssign,      // +=
    [kTokenOpSubAssign] = kOpMinusAssign,  // -=
    [kTokenOpMultAssign] = kOpMultAssign,    // *=
    [kTokenOpDivAssign] = kOpDivAssign,      // /=
    [kTokenOpModAssign] = kOpModAssign,      // %=

    [kTokenOpBitAndAssign] = kOpBitAndAssign,     // &=
    [kTokenOpBitOrAssign] = kOpBitOrAssign,       // |=
    [kTokenOpBitXorAssign] = kOpBitXorAssign,     // ^=
    [kTokenOpShiftLeftAssign] = kOpShiftLeftAssign,   // <<=
    [kTokenOpShiftRightAssign] = kOpShiftRightAssign,  // >>=

    [kTokenQuestion] = kOpCond,          // ?:
    [kTokenMemberAccess] = kOpMemberAccess,  // .
    [kTokenOpPointer] = kOpPointer,        // ->
};

Operator genOperator(TokenType token) { return token_to_operator[token]; }


OpPriority operatorPriority(Operator op) {
  switch (op) {
    case kOpSelfIncSufix:
    case kOpSelfDecSufix:
    case kOpFuncCall:
    case kOpArraySubscript:
    case kOpMemberAccess:
    case kOpPointer:
      return 1;
    case kOpSelfIncPrefix:
    case kOpSelfDecPrefix:
    case kOpPostive:
    case kOpNegative:
    case kOpLogicNot:
    case kOpBitNot:
    case kOpTypeCast:
    case kOpPtrDeref:
    case kOpAddress:
    case kOpSizeof:
      return 2;

    case kOpDiv:
    case kOpMult:
    case kOpReminder:
      return 3;

    case kOpAdd:
    case kOpSub:
      return 4;

    case kOpShiftLeft:
    case kOpShiftRight:
      return 5;

    case kOpGreatThan:
    case kOpGreatEqual:
    case kOpLessThan:
    case kOpLessEqual:
      return 6;

    case kOpEqual:
    case kOpNotEqual:
      return 7;

    case kOpBitAnd:
      return 8;

    case kOpBitXor:
      return 9;

    case kOpBitOr:
      return 10;

    case kOpLogicAnd:
      return 11;

    case kOpLogicOr:
      return 12;

    case kOpCond:
      return 13;

    case kOpAssign:
    case kOpDivAssign:
    case kOpModAssign:
    case kOpMultAssign:
    case kOpAddAssign:
    case kOpMinusAssign:
    case kOpShiftLeftAssign:
    case kOpShiftRightAssign:
    case kOpBitAndAssign:
    case kOpBitXorAssign:
    case kOpBitOrAssign:
      return 14;
    case kOpComma:
      return 15;
    default:
      break;
  }
  return 0;
}

const char *operatorName(Operator op) {
  switch (op) {
    case kOpSelfIncSufix: return "++";
    case kOpSelfDecSufix: return "--";
    case kOpFuncCall:return "()";
    case kOpArraySubscript:return "[";
    case kOpMemberAccess:return ".";
    case kOpPointer:return "->";
    case kOpSelfIncPrefix:return "++";
    case kOpSelfDecPrefix:return "--";
    case kOpPostive:return "+";
    case kOpNegative:return "-";
    case kOpLogicNot:return "!";
    case kOpBitNot:return "~";
    case kOpTypeCast:return "(type)";
    case kOpPtrDeref:return "*";
    case kOpAddress:return "&";
    case kOpSizeof:return "sizeof";

    case kOpDiv:return "/";
    case kOpMult:return "*";
    case kOpReminder:return "%";

    case kOpAdd:return "+";
    case kOpSub:return "-";

    case kOpShiftLeft:return "<<";
    case kOpShiftRight:return ">>";

    case kOpGreatThan:return ">";
    case kOpGreatEqual:return ">=";
    case kOpLessThan:return "<";
    case kOpLessEqual:return "<=";

    case kOpEqual:return "==";
    case kOpNotEqual:return "!=";

    case kOpBitAnd:return "&";

    case kOpBitXor:return "^";

    case kOpBitOr:return "|";

    case kOpLogicAnd:return "&&";

    case kOpLogicOr:return "||";

    case kOpCond:return "?:";

    case kOpAssign:return "=";
    case kOpDivAssign:return "/=";
    case kOpModAssign:return "%=";
    case kOpMultAssign:return "*=";
    case kOpAddAssign:return "+=";
    case kOpMinusAssign:return "-=";
    case kOpShiftLeftAssign:return "<<=";
    case kOpShiftRightAssign:return ">>=";
    case kOpBitAndAssign:return "&=";
    case kOpBitXorAssign:return "^=";
    case kOpBitOrAssign:return "|=";
    case kOpComma:return ",";
    default:break;
  }
  return "";
}


OpAssociative operatorAssociative(Operator op) {
  int p = operatorPriority(op);
  if (p == 2 || p == 13 || p == 14) return kRightToLeft;
  return kLeftToRight;
}

OpType operatorType(Operator op) {
  switch (op) {
    case kOpSelfDecPrefix:
    case kOpSelfIncPrefix:
    case kOpSelfDecSufix:
    case kOpSelfIncSufix:
    case kOpNegative:
    case kOpPostive:
    case kOpPtrDeref:
    case kOpAddress:
    case kOpSizeof:
      return kUnaryOp;
    case kOpCond:
      return kTernaryOp;
  default:
    break;
  }
  return kBinaryOp;
}